package com.bsj.media.media.until;




import com.bsj.media.media.AudioDecorder2;
import com.bsj.media.media.ParserVideoData;
import com.bsj.media.media.PcmEncoderAAC2;
import com.bsj.media.media.resample.resampler;
import com.bsj.power.common.util.ExceptionUtil;
import lombok.extern.slf4j.Slf4j;

import java.nio.ByteBuffer;

/**
 * @Description aac音频处理工具，重采样
 * @Date 2022/10/20 11:26
 **/
@Slf4j
public class AACHandlerUtil {

    public AudioDecorder2 audioDecoderPcm = null; // 音频解码器
    public PcmEncoderAAC2 pcmEncoderAAC = null; // 音频pcm编码成AAC
    public resampler resAudio; // pcm重采样对象
    /**
     * 采样率 、时间刻度
     */
    public int timeScale;
    /**
     * 一个样本字节长度
     */
    public byte oneSampleByteLen;
    /**
     * 音频类型  0 ADPCM_IMA, 1 G711A, 2 G726_32K, 3 AAC, 4 g711u
     */
    public int audioType = -1;

    private byte[] resBuffer; // 重采样的接收buffer，长度用8192
    private int resLen; // buffer 长度
    private byte[] encOneAACNeedPcmSampleBuf = null; // 编码一帧aac，需要pcm的字节数
    private int encOneAACNeedPcmSize = 0; // 编码一帧aac，需要pcm的字节数
    private int encOneAACAlreadySize = 0; // 已经保存的字节大小
    private boolean bDisplay = false;
    /**
     * 用来打印
     */
    private String uri;
    public AACHandlerUtil(int audioType, String uri) {
        this.audioType = audioType;
        this.uri = uri;
        if (audioType == 0 || audioType == 1 || audioType == 2 || audioType == 4) {
            this.audioDecoderPcm = new AudioDecorder2();
            if (!audioDecoderPcm.init(audioType, 1, 8000)) {
                log.error(uri + "初始化解码器失败!");
            }
            this.pcmEncoderAAC = new PcmEncoderAAC2();
            if (!pcmEncoderAAC.init(1, 44100, 64000, 1, 8000)) {
                log.error(uri + "初始化编码器失败!");
            } else {
                if (pcmEncoderAAC.getNeedSampleNum() != 0) {
                    this.encOneAACNeedPcmSize = pcmEncoderAAC.getNeedSampleNum();
                    encOneAACNeedPcmSampleBuf = new byte[this.encOneAACNeedPcmSize];
                    log.info("acc 需要的pcm大小:" + encOneAACNeedPcmSize);
                }
            }
            this.resAudio = new resampler();
            this.resAudio.speex_resampler_init(1, 8000, 44100, 0, 0);
            this.resAudio.speex_resampler_skip_zeros();
            timeScale = 44100;
            oneSampleByteLen = 4;
            this.audioType = audioType;
            this.resLen = 1024 * 10;
            this.resBuffer = new byte[resLen];

        } else if (audioType == 3){
            // aac
            timeScale = 16000;
            oneSampleByteLen = 2;
            this.audioType = audioType;
        }
    }
    /**
     * 处理音频数据
     * @param audioType :  0 ADPCM_IMA, 1 G711A, 2 G726_32K, 3 AAC
     * @param aacDatas : 存放aac数据组，存放 n * (2字节长度 + aac数据)， n表示aacDatas存放多少条aac,
     *                 比如存放一条aac数据:  00 02 aa ac
     *                 存放二条aac数据: 00 02 aa ac 00 04 aa aa aa aa
     * @param body :
     * @param offset :
     * @return
     **/
    public void handlerAudioData(int audioType, ByteBuffer aacDatas, byte[] body, int offset) {
        aacDatas.clear(); // 切换成写模式
        if (audioType != this.audioType) {
            log.error("实时音频类型：" + audioType + " != 初始化音频类型：" + this.audioType);
            return;
        }
        if (audioType == 3) {
            // aac无需重采样
            bodyAudioOfAACHandler(aacDatas, body, offset);
        } else {
            bodyAudioOfOtherHandler(aacDatas, body, offset);
        }
    }
    /**
     * aac无需重采样
     * @param aacDatas:
     * @param body:
     * @param offset:
     * @return
     **/
    private void bodyAudioOfAACHandler(ByteBuffer aacDatas, byte[] body, int offset) {
        int len = body.length - offset;
        int adtsHeaderLen = ParserVideoData.getAdtsAACHeaderLen(body, offset, len);
        int aacLen = ParserVideoData.getAdtsAACRawLen(body, offset, len);
        if (adtsHeaderLen > 0 && aacLen > 0) {
            aacDatas.putShort((short) (aacLen - adtsHeaderLen)); // aac长度 2字节
            aacDatas.put(body, adtsHeaderLen + offset, aacLen - adtsHeaderLen);
        }
    }

    /**
     * 其他音频格式进行重采样
     * @param aacDatas:
     * @param body:
     * @param offset:
     * @return
     **/
    private void bodyAudioOfOtherHandler(ByteBuffer aacDatas, byte[] body, int offset) {
        if (audioDecoderPcm.isInit() && pcmEncoderAAC.isInit()) {
            int mediaLen = body.length - offset;
            int hisiHeaderLen = checkHisiHeader(body, offset, mediaLen);

            // 解码成pcm
            byte[] pcm = new byte[4096];
            int ret = audioDecoderPcm.DecoderSamples(body, offset + hisiHeaderLen, mediaLen - hisiHeaderLen, pcm);
            if (ret > 0) {
                int resapmleLen = resAudio.speex_resampler_process_int(0, pcm, ret / 2, resBuffer, resLen);
                int pcmLen = resapmleLen * 2;
                int pcmOffset = 0;
                while (pcmLen > 0) {
                    if (encOneAACAlreadySize + pcmLen >= encOneAACNeedPcmSize) {
                        int needSampleCopy = encOneAACNeedPcmSize - encOneAACAlreadySize;
                        try {
                            System.arraycopy(resBuffer, pcmOffset, encOneAACNeedPcmSampleBuf, encOneAACAlreadySize,
                                    needSampleCopy);
                        } catch (Exception e) {
                            log.error("拷贝出现异常！ needSampleCopy:"+needSampleCopy+" pcmOffset:"+pcmOffset+" encOneAACAlreadySize:"+encOneAACAlreadySize+
                                    ExceptionUtil.getStackStr(e));
                        }
                        encOneAACAlreadySize = 0;
                        pcmOffset += needSampleCopy;
                        pcmLen -= needSampleCopy;
                        // 把pcm转aac
                        pcmEncoderAAC.encodec(encOneAACNeedPcmSampleBuf,0, encOneAACNeedPcmSize, aacDatas);
                    }else {
                        System.arraycopy(resBuffer, pcmOffset, encOneAACNeedPcmSampleBuf, encOneAACAlreadySize,
                                pcmLen);
                        encOneAACAlreadySize += pcmLen;
                        pcmLen = 0;
                    }
                }
            }
        }
    }
    /**
     * 检验是否有hisi
     * @param body:
     * @param offset:
     * @param mediaLen: 音频长度
     * @return hisi头长度
     **/
    private int checkHisiHeader(byte[] body, int offset, int mediaLen) {
        int realOffset = 0;
        // 4字节海思头
        int lenSize = ((body[offset + 2] & 0xFF) + (body[offset + 3] << 8 & 0xFF)) * 2;
        int audioSize = mediaLen - 4;
        if (lenSize == audioSize) {
            realOffset = 4;
        } else {
            // 没有海思头
            if (!bDisplay) {
                log.warn(uri + "收到音频数据大小不对, hisi头显示大小 ： " + lenSize + ", 实际大小:" + mediaLen);
                bDisplay = true;
            }
        }
        return realOffset;
    }

    /**
     * 释放资源
     * @return
     **/
    public void release() {
        if (encOneAACNeedPcmSampleBuf != null) {
            encOneAACNeedPcmSampleBuf = null;
        }
        if (resBuffer != null) {
            resBuffer = null;
        }
    }
}
